// MIT License
// 开源地址：https://gitee.com/co1024/AbcMvc
// Copyright (c) 2021-2022 1024
// Abc.Mvc=Furion+EF+SqlSugar+Pear layui admin.

using Microsoft.Extensions.Logging;
using Serilog;

namespace Abc.Base;

/// <summary>
/// Cookie 帮助类
/// </summary>
public static class AppEx
{
    #region Furion Log

    /// <summary>
    /// Microsoft.Extensions.Logging.ILogger 静态写日志快捷方式，与Furion提供Log.xxx 有区别，做了一些个性化设置
    /// </summary>
    public static Microsoft.Extensions.Logging.ILogger Log => GetLogger();

    /// <summary>
    ///
    /// </summary>
    /// <param name="floderName">文件夹名称，例如：abc/file</param>
    /// <param name="categoryName">分类</param>
    /// <param name="maxRollingFiles"> 如果指定了该值，那么超出该值将从最初日志文件中从头写入覆盖，默认按一天24个文件，一个小时一个，保存30天</param>
    /// <param name="fileSizeLimitBytes"> 如果指定了该值，那么日志文件大小超出了该配置就会创建的日志文件，新创建的日志文件命名规则：文件名+[递增序号].log，默认100M一个文件</param>
    /// <param name="useUtcTimestamp">是否使用 UTC 时间戳，默认 false</param>
    /// <param name="useUtcTimestamp">最低日志记录级别，默认 Trace</param>
    /// <returns></returns>
    public static Microsoft.Extensions.Logging.ILogger GetLogger(string floderName = "all"
        , string categoryName = "default"
        , int maxRollingFiles = 24 * 30
        , long fileSizeLimitBytes = 100 * 1024 * 1024
        , bool useUtcTimestamp = false
        , LogLevel minimumLevel = LogLevel.Trace)
    {
        var loggerFactory = App.GetRequiredService<ILoggerFactory>(App.RootServices);
        Array.ForEach(new[] { LogLevel.Information, LogLevel.Warning, LogLevel.Error }, logLevel =>
        {
            loggerFactory.AddFile($"logs/{floderName.Replace("\\", "/").Trim('/')}/{1}-{0:yyyy}-{0:MM}-{0:dd}-{0:HH}.log", options =>
            {
                options.FileNameRule = fileName => string.Format(fileName, DateTime.UtcNow, logLevel.ToString());
                options.WriteFilter = logMsg => logMsg.LogLevel == logLevel;
                options.MaxRollingFiles = maxRollingFiles;
                options.FileSizeLimitBytes = fileSizeLimitBytes;
                options.UseUtcTimestamp = useUtcTimestamp;//是否使用 UTC 时间戳，默认 false
                options.MinimumLevel = minimumLevel;
            });
        });

        return loggerFactory.CreateLogger(categoryName);
    }

    #endregion Furion Log

    #region Serilog  日志

    public static readonly string SerilogOutputTemplate = "{NewLine}Date：{Timestamp:yyyy-MM-dd HH:mm:ss.fff} LogLevel：{Level}{NewLine}Message：{Message}{Properties:j}{NewLine}{NewLine}{Exception}" + new string('-', 50);

    /// <summary>
    /// 日志对象集合
    /// </summary>
    private static readonly ConcurrentDictionary<string, Serilog.ILogger> SeriLoggers = new();

    /// <summary>
    /// 获取 Serilog Logger
    /// </summary>
    /// <param name="LogFloderName">日志文件夹名称</param>
    /// <param name="IsLogEventLevelFloder">是否每个事件一个文件夹</param>
    /// <param name="rollOnFileSizeLimit">如果为true，则在达到文件大小限制时将创建一个新文件。文件名将以_NNN格式附加一个数字，第一个文件名没有数字。</param>
    /// <param name="minLogEventLevel">最低日志等级，默认Debug</param>
    /// <param name="rollingInterval">滚动间隔：日志将滚动到新文件的间隔</param>
    /// <param name="retainedFileCountLimit">保留的最大日志文件数，包括当前</param>
    /// <param name="flushToDiskInterval">TimeSpan 不为null，将定期在指定的时间执行完整磁盘刷新间隔。</param>
    /// <param name="buffered">指示是否可以缓存到输出文件。默认值 false</param>
    /// <param name="shared">允许多个进程共享日志文件。默认值为false</param>
    /// <param name="fileSizeLimitBytes">文件大小限制字节：允许日志文件访问的近似最大大小（以字节为单位）成长。对于不受限制的增长，传递null。默认值为1 GB。避免写作部分事件，限制内的最后一个事件将全部写入偶数如果超过限制</param>
    /// <param name="outputTemplate">日志输出模板</param>
    /// <param name="filename">文件名</param>
    /// <returns></returns>
    public static Serilog.ILogger GetSeriLogger(
                                    // string path,
                                    // LogEventLevel restrictedToMinimumLevel = LogEventLevel.Verbose,

                                    // LoggingLevelSwitch levelSwitch = null,

                                    string logFloderName = "default",//日志文件夹名称
                                    bool isLogEventLevelFloder = true,//是否按日志类别分类记录
                                                                      //ITextFormatter formatter,
                                    bool rollOnFileSizeLimit = true,//如果为true，则在达到文件大小限制时将创建一个新文件。文件名将以_NNN格式附加一个数字，第一个文件名没有数字。
                                                                    // Encoding encoding = null,
                                                                    // // FileLifecycleHooks hooks = null,
                                                                    //
                                                                    //
                                    Serilog.Events.LogEventLevel minLogEventLevel = Serilog.Events.LogEventLevel.Debug,//最低日志等级，默认Debug
                                     Serilog.RollingInterval rollingInterval = Serilog.RollingInterval.Hour,//滚动间隔：日志将滚动到新文件的间隔
                                    int? retainedFileCountLimit = null,//保留的最大日志文件数，包括当前
                                    TimeSpan? flushToDiskInterval = null,//TimeSpan 不为null，将定期在指定的时间执行完整磁盘刷新间隔。
                                    bool buffered = false,//指示是否可以缓存到输出文件。默认值 false
                                    bool shared = false,//允许多个进程共享日志文件。默认值为false
                                    long? fileSizeLimitBytes = 1073741824L,//文件大小限制字节：允许日志文件访问的近似最大大小（以字节为单位）成长。对于不受限制的增长，传递null。默认值为1 GB。避免写作部分事件，限制内的最后一个事件将全部写入偶数如果超过限制
                                    string outputTemplate = "",
                                    string filename = "")
    {
        //判断日志文件夹设置是否为空，如果为空，设置一个默认文件夹
        if (string.IsNullOrWhiteSpace(logFloderName))
        {
            logFloderName = "default";
        }
        else
        {
            logFloderName = logFloderName.Trim("\\");
        }
        //对自定义文件名进行处理，去除末尾的 反斜杠字符
        if (!string.IsNullOrWhiteSpace(filename))
        {
            filename = filename.Trim("\\");
        }
        //判断缓存的 ILogger 是否已存在，如果改以存在从字典集合直接取出使用，不在重新创建
        Serilog.ILogger logger;
        if (SeriLoggers.ContainsKey(logFloderName))
        {
            _ = SeriLoggers.TryGetValue(logFloderName, out logger);
        }
        else
        {
            outputTemplate = SerilogOutputTemplate;//日志输出模板
            if (!string.IsNullOrWhiteSpace(App.Configuration["Serilog:Customer:OutputTemplate"]))
            {
                outputTemplate = App.Configuration["Serilog:Customer:OutputTemplate"];
            }

            try
            {
                // if (string.IsNullOrWhiteSpace(outputTemplate)) outputTemplate = LogTemplate;
                //从配置文件取出配置数据
                //最低日志等级，默认Debug
                minLogEventLevel = App.Configuration["Serilog:Customer:MinLogEventLevel"]?.To<Serilog.Events.LogEventLevel>() ?? Serilog.Events.LogEventLevel.Debug;
                //滚动间隔：日志将滚动到新文件的间隔
                rollingInterval = App.Configuration["Serilog:Customer:RollingInterval"]?.To<Serilog.RollingInterval>() ?? Serilog.RollingInterval.Hour;
                //如果为true，则在达到文件大小限制时将创建一个新文件。文件名将以_NNN格式附加一个数字，第一个文件名没有数字。
                rollOnFileSizeLimit = App.Configuration["Serilog:Customer:RollOnFileSizeLimit"]?.To<bool>() ?? true;
                //指示是否可以缓存到输出文件。默认值 false
                buffered = App.Configuration["Serilog:Customer:Buffered"]?.To<bool>() ?? false;
                //允许多个进程共享日志文件。默认值为false
                shared = App.Configuration["Serilog:Customer:Shared"]?.To<bool>() ?? false;

                //文件大小限制字节：允许日志文件访问的近似最大大小（以字节为单位）成长。对于不受限制的增长，传递null。默认值为1 GB。避免写作部分事件，限制内的最后一个事件将全部写入偶数如果超过限制
                var tempFileSizeLimitBytes = App.Configuration["Serilog:Customer:FileSizeLimitBytes"];
                if (!string.IsNullOrWhiteSpace(tempFileSizeLimitBytes))
                {
                    fileSizeLimitBytes = tempFileSizeLimitBytes.To<long>();
                }
                //保留的最大日志文件数，包括当前
                var tempRetainedFileCountLimit = App.Configuration["Serilog:Customer:RetainedFileCountLimit"];
                if (!string.IsNullOrWhiteSpace(tempRetainedFileCountLimit))
                {
                    retainedFileCountLimit = tempRetainedFileCountLimit.To<int>();
                }
                //TimeSpan 不为null，将定期在指定的时间执行完整磁盘刷新间隔。
                var tempFlushToDiskInterval = App.Configuration["Serilog:Customer:FlushToDiskInterval"];
                if (!string.IsNullOrWhiteSpace(tempFlushToDiskInterval))
                {
                    flushToDiskInterval = tempFlushToDiskInterval.To<TimeSpan>();
                }
                //是否按日志类别分类记录
                isLogEventLevelFloder = App.Configuration["Serilog:Customer:IsLogEventLevelFloder"].To<bool>();
            }
            catch (Exception)
            {
            }
            if (isLogEventLevelFloder)//是否按日志类别分类记录
            {
                logger = new Serilog.LoggerConfiguration()
                    .SetSeriLogMinimumLevel(minLogEventLevel)
                    .SetWriteTo(logFloderName,//日志文件夹名称
                                              //ITextFormatter formatter,
                                              rollOnFileSizeLimit,
                                              // Encoding encoding = null,
                                              // FileLifecycleHooks hooks = null,
                                              minLogEventLevel,
                                              rollingInterval,
                                              retainedFileCountLimit,
                                              flushToDiskInterval,
                                              buffered,
                                              shared,
                                              fileSizeLimitBytes,
                                              outputTemplate,
                                              filename)
                    .CreateLogger();
            }
            else
            {
                //日志都记录到一个日志文件中
                logger = new Serilog.LoggerConfiguration()
                                             .SetSeriLogMinimumLevel(minLogEventLevel)
                                             .WriteTo.File($"logs\\{logFloderName}\\log.txt", rollingInterval: rollingInterval, rollOnFileSizeLimit: rollOnFileSizeLimit, buffered: buffered, shared: shared, fileSizeLimitBytes: fileSizeLimitBytes,
                                                 retainedFileCountLimit: retainedFileCountLimit, flushToDiskInterval: flushToDiskInterval, outputTemplate: outputTemplate)
                                             .CreateLogger();
            }
            SeriLoggers.TryAdd(logFloderName, logger);
        }
        if (logger == null) logger = Serilog.Log.Logger;
        return logger;
    }

    private static Serilog.LoggerConfiguration SetWriteTo(this Serilog.LoggerConfiguration loggerConfiguration,// string path,
                                                                                                               // LogEventLevel restrictedToMinimumLevel = LogEventLevel.Verbose,

                                    // LoggingLevelSwitch levelSwitch = null,
                                    string LogFloderName = "",//日志文件夹名称

                                    bool rollOnFileSizeLimit = true,//如果为true，则在达到文件大小限制时将创建一个新文件。文件名将以_NNN格式附加一个数字，第一个文件名没有数字。
                                                                    // Encoding encoding = null,
                                                                    // FileLifecycleHooks hooks = null,
                                    Serilog.Events.LogEventLevel minLogEventLevel = Serilog.Events.LogEventLevel.Debug,//最低日志等级，默认Debug
                                    Serilog.RollingInterval rollingInterval = Serilog.RollingInterval.Hour,//滚动间隔：日志将滚动到新文件的间隔
                                    int? retainedFileCountLimit = 100,//保留的最大日志文件数，包括当前
                                    TimeSpan? flushToDiskInterval = null,//TimeSpan 不为null，将定期在指定的时间执行完整磁盘刷新间隔。
                                    bool buffered = false,//指示是否可以缓存到输出文件。默认值 false
                                    bool shared = false,//允许多个进程共享日志文件。默认值为false
                                    long? fileSizeLimitBytes = 1073741824L,//文件大小限制字节：允许日志文件访问的近似最大大小（以字节为单位）成长。对于不受限制的增长，传递null。默认值为1 GB。避免写作部分事件，限制内的最后一个事件将全部写入偶数如果超过限制
                                    string outputTemplate = "",
                                    string filename = "")
    {
        //日志是否输出到控制台
        var isConsole = App.Configuration["Serilog:Customer:IsConsole"]?.To<bool>() ?? false;
        if (isConsole)
        {
            loggerConfiguration.WriteTo.Console(outputTemplate: outputTemplate);
        }
        //日志是否写入到文件
        var isFile = App.Configuration["Serilog:Customer:IsFile"]?.To<bool>() ?? true;
        if (isFile)
        {
            foreach (Serilog.Events.LogEventLevel logEventLevel in Enum.GetValues(typeof(Serilog.Events.LogEventLevel)))
            {
                loggerConfiguration.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == logEventLevel)
                    .WriteTo.File(SeriLogLogFilePath(LogFloderName, logEventLevel, filename),
                        rollingInterval: rollingInterval, outputTemplate: outputTemplate,
                        rollOnFileSizeLimit: rollOnFileSizeLimit, fileSizeLimitBytes: fileSizeLimitBytes,
                        retainedFileCountLimit: retainedFileCountLimit, flushToDiskInterval: flushToDiskInterval,
                        shared: shared));
            }
        }
        //日志是否保存到MSSQL数据库
        var isMsSql = App.Configuration["Serilog:Customer:IsMsSql"]?.To<bool>() ?? false;
        if (isMsSql)
        {
            //var columnOpts = new ColumnOptions();
            //columnOpts.Store.Remove(StandardColumn.Properties);
            //columnOpts.Store.Add(StandardColumn.LogEvent);
            //columnOpts.LogEvent.DataLength = 2048;
            //columnOpts.PrimaryKey = options.TimeStamp;
            //columnOpts.TimeStamp.NonClusteredIndex = true;
            var columnOptions = new Serilog.Sinks.MSSqlServer.ColumnOptions();
            //连接字符串
            var connectionString = App.Configuration["Serilog:Customer:MSSQLSERVER:ConnectionString"];
            //表名
            var tableName = App.Configuration["Serilog:Customer:MSSQLSERVER:TableName"] ?? "SeriLogs";
            //    限制每个批将多少日志事件写入数据库（默认值：50）
            var batchPostingLimit = App.Configuration["Serilog:Customer:MSSQLSERVER:batchPostingLimit"]?.ToInt32() ?? 50;
            //记录日志最低等级
            var minimumLevel = App.Configuration["Serilog:Customer:MSSQLSERVER:restrictedToMinimumLevel"]?.To<Serilog.Events.LogEventLevel>() ?? Serilog.Events.LogEventLevel.Verbose;

            //选项
            var sinkOpts = new Serilog.Sinks.MSSqlServer.MSSqlServerSinkOptions
            {
                TableName = $"{tableName}-{DateTimeOffset.Now:yyyyMMdd}",
                BatchPostingLimit = batchPostingLimit,
                BatchPeriod = TimeSpan.FromSeconds(3),//执行时间间隔
                AutoCreateSqlTable = true
            };
            //写入到数据库
            loggerConfiguration.WriteTo.MSSqlServer(
                connectionString: connectionString,
                sinkOptions: sinkOpts,
                // tableName: tableName,
                columnOptions: columnOptions,
                // batchPostingLimit: batchPostingLimit,//批量插入数据库条数
                // period: TimeSpan.FromSeconds(5),//执行时间间隔
                restrictedToMinimumLevel: minimumLevel
            //autoCreateSqlTable: true
            );
        }

        return loggerConfiguration;
    }

    private static Serilog.LoggerConfiguration SetSeriLogMinimumLevel(this Serilog.LoggerConfiguration loggerConfiguration, Serilog.Events.LogEventLevel minLevel)
    {
        switch (minLevel)
        {
            case Serilog.Events.LogEventLevel.Verbose://冗余的
                return loggerConfiguration.MinimumLevel.Verbose();

            case Serilog.Events.LogEventLevel.Debug://调试
                return loggerConfiguration.MinimumLevel.Debug();

            case Serilog.Events.LogEventLevel.Information://信息
                return loggerConfiguration.MinimumLevel.Information();

            case Serilog.Events.LogEventLevel.Warning://警告
                return loggerConfiguration.MinimumLevel.Warning();

            case Serilog.Events.LogEventLevel.Error://错误
                return loggerConfiguration.MinimumLevel.Error();

            case Serilog.Events.LogEventLevel.Fatal://致命
                return loggerConfiguration.MinimumLevel.Fatal();

            default:
                break;
        }
        return loggerConfiguration.MinimumLevel.Debug();
    }

    private static string SeriLogLogFilePath(string LogFloderName, Serilog.Events.LogEventLevel loglevel, string filename = "")
    {
        var date = DateTime.Now.ToString("yyyy-MM-dd");//按时间创建文件夹
        if (!string.IsNullOrWhiteSpace(filename))
        {
            return $"logs\\{LogFloderName}\\{date}\\{filename}_.txt";
        }
        else
        {
            return $"logs\\{LogFloderName}\\{date}\\{loglevel:G}_.txt";
        }
    }

    /// <summary>
    /// 获取Serilog 自定义日志配置
    /// </summary>
    /// <returns></returns>
    public static void GetSeriLogDefaultConfiguration(LoggerConfiguration config)
    {
        var date = DateTime.Now.ToString("yyyy-MM-dd");//按时间创建文件夹
        config.WriteTo.Console(outputTemplate: SerilogOutputTemplate)
              .WriteTo.File($"logs\\all\\{date}\\log_.txt", rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, fileSizeLimitBytes: 20971520, retainedFileCountLimit: 100, shared: true);
        foreach (Serilog.Events.LogEventLevel logEventLevel in Enum.GetValues(typeof(Serilog.Events.LogEventLevel)))
        {
            config.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == logEventLevel)
              .WriteTo.File(SeriLogLogFilePath("all", logEventLevel), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate, rollOnFileSizeLimit: true, fileSizeLimitBytes: 20971520, shared: true));
        }
    }

    #endregion Serilog  日志

    #region NLog(未使用，作为历史代码)

    //private static readonly ConcurrentDictionary<string, NLog.Logger> NLoggers = new();
    //private static readonly Dictionary<string, string> dictionary = new();

    /////// <summary>
    /////// 全局属性参数【初始化设定】
    /////// </summary>
    ////public static Dictionary<string, string> GlobalProperties = dictionary;

    ///// <summary>
    ///// 获取Logger ，按 logFloder 昨晚Key缓存
    ///// </summary>
    ///// <param name="logFloder"></param>
    ///// <param name="filename"></param>
    ///// <param name="withPropertys">字典</param>
    ///// <returns></returns>
    //public static NLog.Logger GetNLogger(
    //    string logFloder = "default",
    //    string filename = "",
    //    Dictionary<string, string> withPropertys = null)
    //{
    //    if (string.IsNullOrWhiteSpace(logFloder))
    //    {
    //        logFloder = "default";
    //    }
    //    Logger logger;
    //    //从缓存中取出日志对象 logger
    //    if (NLoggers.ContainsKey(logFloder))
    //    {
    //        _ = NLoggers.TryGetValue(logFloder, out logger);
    //    }
    //    else
    //    {
    //        //创建一个新的日志对象 logger
    //        //logger = LogManager.GetCurrentClassLogger();
    //        logger = LogManager.GetLogger(logFloder);
    //        //if (string.IsNullOrWhiteSpace(logFloder)) logFloder = "default";
    //        PropertiesAdd(logger, "logfloder", logFloder.Trim());
    //        if (!string.IsNullOrWhiteSpace(filename)) PropertiesAdd(logger, "filename", $"{filename.Trim()}-");

    //        if (withPropertys != null)
    //        {
    //            foreach (var withProperty in withPropertys)
    //            {
    //                if (string.IsNullOrWhiteSpace(withProperty.Key)) continue;
    //                PropertiesAdd(logger, withProperty.Key.Trim(), withProperty.Value?.Trim() ?? "");
    //            }
    //        }
    //        NLoggers.TryAdd(logFloder, logger);
    //    }
    //    return logger;
    //}

    //private static NLog.Logger PropertiesAdd(NLog.Logger logger, string key, object value)
    //{
    //    if (logger.Properties.ContainsKey(key)) return logger;
    //    logger.Properties.Add(key, value);
    //    return logger;
    //}

    #endregion NLog(未使用，作为历史代码)

    #region 访问计数，访问防重复执行

    /// <summary>
    /// 访问计数，防止锁死
    /// </summary>
    private static readonly ConcurrentDictionary<string, int> MethodUses = new();

    ///// <summary>
    ///// 允许多线程执行
    ///// </summary>
    ///// <returns></returns>
    //public static int AllowMultithreadingExec()
    //{
    //}

    /// <summary>
    /// 获取访问次数
    /// 每次调用方法，对应key的值都会加 1，创建的新值默认为1
    /// </summary>
    /// <param name="key">关键词</param>
    /// <param name="maxcount">最大计数，超过此计数会清0</param>
    /// <returns></returns>
    public static int AllowMultithreadingExec(string key, int maxcount = -1)
    {
        if (MethodUses.ContainsKey(key))
        {
            MethodUses[key] = MethodUses[key] + 1;
            if (maxcount > 0 && MethodUses[key] > maxcount)
            {
                MethodUses[key] = 0;
                return 0;
            }
            else
            {
                return MethodUses[key];
            }
        }
        else
        {
            MethodUses.TryAdd(key, 1);
            return 1;
        }
    }

    /// <summary>
    /// 清理访问次数，清理后的访问次数为0
    /// </summary>
    /// <param name="key"></param>
    public static void AllowMultithreadingExecClear(string key)
    {
        if (MethodUses.ContainsKey(key))
        {
            MethodUses[key] = 0;
        }
    }

    #endregion 访问计数，访问防重复执行

    ///// <summary>
    ///// Web 主机注入
    ///// </summary>
    ///// <param name="hostBuilder">Web主机构建器</param>
    ///// <param name="assemblyName">外部程序集名称</param>
    ///// <returns>IWebHostBuilder</returns>
    //public static IWebHostBuilder Inject(this IWebHostBuilder hostBuilder, string assemblyName = nameof(Furion))
    //{
    //    hostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, assemblyName);
    //    return hostBuilder;
    //}

    ///// <summary>
    ///// 获取  System.Text.Json 默认设置
    ///// </summary>
    ///// <returns></returns>
    //public static System.Text.Json.JsonSerializerOptions GetDefaultJsonSerializerOptions()
    //{
    //    var options = new JsonSerializerOptions();
    //    options.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All);//设置虚拟化中文的时候不编码
    //    return options;
    //}

    /// <summary>
    /// 获取上传文件的文件夹
    /// </summary>
    /// <returns></returns>
    public static string GetUploadDefaultFloder()
    {
        //默认物理文件夹
        var defaultPhysicalPath = App.Configuration["AppInfo:WebUploadFloder"];
        if (string.IsNullOrWhiteSpace(defaultPhysicalPath) || !Directory.Exists(defaultPhysicalPath))
        {
            //如果不存在，设置默认的物理文件，网站更目录下
            if (App.WebHostEnvironment != null)
            {
                defaultPhysicalPath = App.WebHostEnvironment.ContentRootPath;
            }
            else if (App.HostEnvironment != null)
            {
                defaultPhysicalPath = App.HostEnvironment.ContentRootPath;
            }
            else
            {
                throw Oops.Bah("WebHostEnvironment 和 HostEnvironment 都为空了");
            }
        }

        return defaultPhysicalPath;
    }

    /// <summary>
    /// 获取上传文件完整路径
    /// </summary>
    /// <param name="fileRelativePath">文件相对路径（例如：/u/f/20220101/xxxx.png）</param>
    /// <param name="logicalPath">URL访问的逻辑路径 域名/{逻辑路径}/文件相对路径 （默认:/u/f）</param>
    /// <returns></returns>
    public static string GetUploadFileFullPath(string fileRelativePath, string logicalPath = "/u/f")
    {
        var floder = GetUploadDefaultFloder();
        //拼接完整文件路径

        //获取魔法路径
        logicalPath = GetWebUploadLogicalPath(logicalPath);
        var filepath = "";
        if (fileRelativePath.StartsWith(logicalPath))
        {
            filepath = Path.Combine(floder, "upload", $"{fileRelativePath.TrimStart(logicalPath).Replace("/", "\\").TrimStart("\\")}");
        }
        else
        {
            filepath = Path.Combine(floder, $"{fileRelativePath.Replace("/", "\\").TrimStart("\\")}");
        }

        if (!System.IO.File.Exists(filepath)) return null;

        return filepath;
    }

    /// <summary>
    /// 获取文件上传魔法路径
    /// 例如 ：/u/f/abc/20220101/xxx.png
    /// 魔法路径：/u/f
    /// </summary>
    /// <param name="logicalPath">魔法路径，默认为:/u/f</param>
    /// <returns></returns>
    public static string GetWebUploadLogicalPath(string logicalPath = "/u/f")
    {
        var logicalPathTemp = App.Configuration["AppInfo:WebUploadLogicalPath"];
        if (!string.IsNullOrWhiteSpace(logicalPathTemp))
        {
            logicalPath = logicalPathTemp;
        }
        return logicalPath;
    }

    /// <summary>
    /// 获取配置(默认)
    /// </summary>
    /// <typeparam name="TOptions">强类型选项类</typeparam>
    /// <param name="loadPostConfigure"></param>
    /// <returns>TOptions</returns>
    public static TOptions GetConfig<TOptions>(bool loadPostConfigure = false)
    {
        var path = typeof(TOptions).Name;
        if (path.EndsWith("Options"))
        {
            path = path.ReplaceLast("Options", "");
        }
        return App.GetConfig<TOptions>(path);
    }
}